#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

#include <linux/delay.h>


struct device_test {
    dev_t dev_num;              // 设备号
    int major;                  // 主设备号
    int minor;                  // 次设备号
    struct cdev cdev_test;      // 字符设备
    struct class *class;        // 类
    struct device *device;      // 设备

    char kbuf[32];              // 缓存区buf 
    unsigned int *vir_gpio_dr;  // 虚拟GPIO数据寄存器
};


// 定义设备
struct device_test dev1;


/* 打开设备函数*/
static int cdev_open(struct inode *inode, struct file *file)
{

    //inode->i_rdev为该inode的字符设备，
    // 使用container_of函数通过字符设备找到设备结构体变量dev1的地址,并设置为私有数据
    file->private_data = container_of(inode->i_cdev, struct device_test, cdev_test);

    printk(KERN_EMERG "This is cdev_open\r\n");
    return 0;
}




/* 向设备写数据函数*/
static ssize_t cdev_write(struct file *file, const char __user *buff, size_t size, loff_t * off)
{
    struct device_test *test_dev = (struct device_test *)file->private_data;

    // 用户向内核空间传递数据
    if (copy_from_user(test_dev->kbuf, buff, size) != 0)
    {
        printk(KERN_EMERG "copy_from_user error\r\n");
        return -1;
    }

    if (strcmp(test_dev->kbuf, "test1") == 0)
    {
        ssleep(4);
    }
    else if (strcmp(test_dev->kbuf, "test2") == 0)
    {
        ssleep(2);
    }

    printk(KERN_EMERG "cdev_write kbuf size : %ld\r\n", size);
    printk(KERN_EMERG "kbuf: %s\r\n", test_dev->kbuf);

    return 0;
}

/* 从设备读数据 函数 */
static ssize_t cdev_read(struct file *file, char __user *buff, size_t size, loff_t *off)
{
    struct device_test *test_dev = (struct device_test *)file->private_data;

    // 内核 向 用户空间传递数据
    if (copy_to_user(buff, test_dev->kbuf, size) != 0)
    {
        printk(KERN_EMERG "copy_to_user error\r\n");
        return -1;
    }
    printk(KERN_EMERG "This is cdev_read\r\n");
    return 0;
}


static int cdev_release (struct inode *inode, struct file *file)
{
    printk(KERN_EMERG "This is cdev_release\r\n");
    return 0;
}


struct file_operations cdev_fops = {
    .owner = THIS_MODULE,
    .open = cdev_open,
    .read = cdev_read,
    .write = cdev_write,
    .release = cdev_release,
};


static int __init chrdev_init(void)
{
    int ret ;

    // 注册设备号
    ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "chrdev_test");
    if (ret < 0)
    {
        printk(KERN_EMERG "alloc chrdev is error\n");
        goto err_cdev_region;
    }
    printk(KERN_EMERG "alloc chrdev is ok\n");

    dev1.major = MAJOR(dev1.dev_num);
    dev1.minor = MINOR(dev1.dev_num);
    printk(KERN_EMERG "major = %d  | minor = %d\n", dev1.major, dev1.minor);

    // 初始化，添加字符设备
    dev1.cdev_test.owner = THIS_MODULE;
    cdev_init(&dev1.cdev_test, &cdev_fops);
    ret = cdev_add(&dev1.cdev_test, dev1.dev_num, 1);
    if (ret < 0)
    {
        goto err_cdev_add;
    }

    // 创建类和设备节点
    dev1.class = class_create(THIS_MODULE, "gpio_test");
    if (IS_ERR(dev1.class))
    {
        ret = PTR_ERR(dev1.class);
        goto err_class_create;
    }
    dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "gpio_test");
    if (IS_ERR(dev1.device))
    {
        ret = PTR_ERR(dev1.device);
        goto err_device_create;
    }

    return 0;

err_device_create:
    class_destroy(dev1.class);                      // 删除类
err_class_create:    
    cdev_del(&dev1.cdev_test);                      // 删除字符设备
err_cdev_add:
    unregister_chrdev_region(dev1.dev_num, 1);      // 删除设备号
err_cdev_region:
    return ret;                                     // 返回错误码
}


static void __exit chrdev_exit(void)
{
    device_destroy(dev1.class, dev1.dev_num);       // 删除设备节点

    class_destroy(dev1.class);                      // 删除类

    cdev_del(&dev1.cdev_test);                      // 删除字符设备

    unregister_chrdev_region(dev1.dev_num, 1);      // 删除设备号

    printk(KERN_EMERG "module exit\r\n");
}


module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("treasure1029");
